import java.io.*;
import java.net.*;
import java.awt.*;
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.Timer;
import java.awt.image.*;
import javax.imageio.*;
import javax.imageio.stream.ImageOutputStream;

public class Server extends JFrame implements ActionListener {

    //RTP 变量:
    DatagramSocket RTPsocket; //发送和接受udp包
    DatagramPacket senddp; //UDP包，包含视频帧

    InetAddress ClientIPAddr;   //客户端IP地址
    int RTP_dest_port = 0;      // Rtp包的目的地址(由rtSP客户端提供)
    int RTSP_dest_port = 0;

    //界面:
    JLabel label;

    //视频:
    int imageNb = 0;            //正在传输的图片序号
    VideoStream video; //访问视频帧的video stream对象
    static int MJPEG_TYPE = 26;             //RTP payload -》 MJPEG 格式
    static int FRAME_PERIOD = 100; //视频帧间隔，ms







    static int VIDEO_LENGTH = 498; //视频长度为498帧







    Timer timer;         //用于以视频帧率发送图像的定时器
    byte[] buf;       //用于存储发送给客户端的图像的缓冲区
    int sendDelay;  //发送间隔

    
    //rtsp 状态
    final static int INIT = 0;
    final static int READY = 1;
    final static int PLAYING = 2;
    //rtsp 消息类型
    final static int SETUP = 3;
    final static int PLAY = 4;
    final static int PAUSE = 5;
    final static int TEARDOWN = 6;
    final static int DESCRIBE = 7;

    static int state;  //RTSP Server 状态 : INIT / READY / PLAY
    Socket RTSPsocket;      //用于发送/接收RTSP信息
     //输入和输出流过滤
    static BufferedReader RTSPBufferedReader;
    static BufferedWriter RTSPBufferedWriter;
    static String VideoFileName;                     //客户端提供的视频文件信息
    static String RTSPid = UUID.randomUUID().toString(); //RTSP 唯一的ID
    int RTSPSeqNb = 0; //会话中RTSP信息的序列号


    //RTCP 变量
    static int RTCP_RCV_PORT = 19001; //客户端接收RTCP数据包的端口
    static int RTCP_PERIOD = 400;     //检查控制信息的频率
    DatagramSocket RTCPsocket;
    RtcpReceiver rtcpReceiver;
    int congestionLevel;

    //性能优化和控制
    ImageTranslator imgTranslator;

    final static String CRLF = "\r\n";

    
    public Server() {
        super("RTSP 服务器");

        //初始化RTP发送计时器
        sendDelay = FRAME_PERIOD;
        timer = new Timer(sendDelay, this);
        timer.setInitialDelay(0);
        timer.setCoalesce(true);

       

        //为发送缓冲区分配内存
        buf = new byte[60000];

        //关闭主窗口
        addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                timer.stop();
                rtcpReceiver.stopRcv();
                System.exit(0);
            }
        });

        //启动RTCP数据包接收器
        rtcpReceiver = new RtcpReceiver(RTCP_PERIOD);

        //GUI:
        label = new JLabel("发送了 ： 0帧  ", JLabel.CENTER);
        getContentPane().add(label, BorderLayout.CENTER);

        //视频编码质量
        imgTranslator = new ImageTranslator(0.8f);
    }

    //main
    public static void main(String[] argv) throws Exception {
        //创建服务器对象
        Server server = new Server();

        //GUI
        server.pack();
        server.setVisible(true);
        server.setSize(new Dimension(400, 200));

        //定义rtsp client端口
        int RTSPport = 1051;
        server.RTSP_dest_port = RTSPport;

        //为RTSP会话发起与客户端的TCP连接
        ServerSocket listenSocket = new ServerSocket(RTSPport);
        server.RTSPsocket = listenSocket.accept();
        listenSocket.close();

        //获取client IP地址
        server.ClientIPAddr = server.RTSPsocket.getInetAddress();

        //初始化rtsp状态
        state = INIT;

        RTSPBufferedReader = new BufferedReader(new InputStreamReader(server.RTSPsocket.getInputStream()));
        RTSPBufferedWriter = new BufferedWriter(new OutputStreamWriter(server.RTSPsocket.getOutputStream()));

        //等待客户端SETUP请求
        int request_type;
        boolean done = false;
        while (!done) {
            request_type = server.parseRequest(); //阻塞

            if (request_type == SETUP) {
                done = true;

                //更新rtsp状态
                state = READY;
                System.out.println("New RTSP state: READY");

                //发送回应
                server.sendResponse();

                //创建VideoStream对象
                server.video = new VideoStream(VideoFileName);

                //rtp端口号由客户端给定，rtcp端口号已给出
                server.RTPsocket = new DatagramSocket();
                server.RTCPsocket = new DatagramSocket(RTCP_RCV_PORT);
            }
        }

        //循环处理rtsp请求
        while (true) {

            request_type = server.parseRequest(); //阻塞

            if ((request_type == PLAY) && (state == READY)) {

                server.sendResponse();

                server.timer.start();
                server.rtcpReceiver.startRcv();

                state = PLAYING;
                System.out.println("New RTSP state: PLAYING");
            } else if ((request_type == PAUSE) && (state == PLAYING)) {
                //发送回应
                server.sendResponse();
                //停止计时器
                server.timer.stop();
                server.rtcpReceiver.stopRcv();
                //更新状态
                state = READY;
                System.out.println("RTSP 新状态: READY");
            } else if (request_type == TEARDOWN) {
                server.sendResponse();
                server.timer.stop();
                server.rtcpReceiver.stopRcv();
                //关闭链接
                server.RTSPsocket.close();
                server.RTPsocket.close();

                System.exit(0);
            } else if (request_type == DESCRIBE) {
                System.out.println("收到 DESCRIBE 请求");
                server.sendDescribe();
            }
        }
    }

    //定时器
    public void actionPerformed(ActionEvent e) {
        byte[] frame;

        //如果当前图像number小于视频的长度,未播放完
        if (imageNb < VIDEO_LENGTH) {
            //更新当前nb
            imageNb++;

            try {
                //从视频中获得下一帧要发送的信息，以及它的大小
                int image_length = video.getNextFrame(buf);

                //如果检测到有拥塞，则调整图像的质量
                if (congestionLevel > 0) {
//                    imgTranslator.setCompressionQuality();

                } else {
                    //调整图像质量
                    imgTranslator.setCompressionQuality(0.5f);
                    frame = imgTranslator.compress(Arrays.copyOfRange(buf, 0, image_length));
                    image_length = frame.length;
                    System.arraycopy(frame, 0, buf, 0, image_length);
                }

                //构建一个包含帧的RTPpacket对象
                RTPpacket rtp_packet = new RTPpacket(MJPEG_TYPE, imageNb, imageNb * FRAME_PERIOD, buf, image_length);

                //得到要发送的完整rtp数据包的总长度
                int packet_length = rtp_packet.getLength();

                //检索数据包位流并将其存储在一个字节数组中
                byte[] packet_bits = new byte[packet_length];
                rtp_packet.getPacket(packet_bits);

                //通过UDP套接字将数据包作为DatagramPacket发送
                senddp = new DatagramPacket(packet_bits, packet_length, ClientIPAddr, RTP_dest_port);
                RTPsocket.send(senddp);

                System.out.println("Send frame ：" + imageNb + ", Frame size: " + image_length + " (" + buf.length + ")");
                //打印头文件的比特流
                rtp_packet.printheader();

                //更新 GUI
                label.setText("发送了：" + imageNb + "帧");
            } catch (Exception ex) {
                ex.printStackTrace();
                System.out.println("Exception : " + ex);
                System.exit(0);
            }
        } else {
            //如果是视频文件的末尾，停止定时器
            timer.stop();
            rtcpReceiver.stopRcv();
            System.exit(0);
        }
    }

           //监听从客户端发来的RTCP数据包
    class RtcpReceiver implements ActionListener {
        private Timer rtcpTimer;
        private byte[] rtcpBuf;
        int interval;

        public RtcpReceiver(int interval) {
            //持续接受客户端rtcp数据包
            this.interval = interval;
            rtcpTimer = new Timer(interval, this);
            rtcpTimer.setInitialDelay(0);
            rtcpTimer.setCoalesce(true);

            //为接收RTCP数据包分配缓冲区
            rtcpBuf = new byte[512];
        }

        public void actionPerformed(ActionEvent e) {
            //构建一个DatagramPacket从UDP套接字中接收数据
            DatagramPacket dp = new DatagramPacket(rtcpBuf, rtcpBuf.length);
            float fractionLost;

            try {
                RTCPsocket.receive(dp);   // 阻塞
                RTCPpacket rtcpPkt = new RTCPpacket(dp.getData(), dp.getLength());
                System.out.println("[RTCP] " + rtcpPkt);

            } catch (InterruptedIOException iioe) {
                System.out.println("没有数据");
            } catch (IOException ioe) {
                System.out.println("Exception : " + ioe);
            }
        }

        public void startRcv() {
            rtcpTimer.start();
        }

        public void stopRcv() {
            rtcpTimer.stop();
        }
    }

    //将图像转化成不同的编码或质量
    class ImageTranslator {

        private float compressionQuality;
        private ByteArrayOutputStream baos;
        private BufferedImage image;
        private Iterator<ImageWriter> writers;
        private ImageWriter writer;
        private ImageWriteParam param;
        private ImageOutputStream ios;

        public ImageTranslator(float cq) {
            compressionQuality = cq;

            try {
                baos = new ByteArrayOutputStream();
                ios = ImageIO.createImageOutputStream(baos);

                writers = ImageIO.getImageWritersByFormatName("jpeg");
                writer = (ImageWriter) writers.next();
                writer.setOutput(ios);

                param = writer.getDefaultWriteParam();
                param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                param.setCompressionQuality(compressionQuality);

            } catch (Exception ex) {
                System.out.println("Exception : " + ex);
                System.exit(0);
            }
        }

        public byte[] compress(byte[] imageBytes) {
            try {
                baos.reset();
                image = ImageIO.read(new ByteArrayInputStream(imageBytes));
                writer.write(null, new IIOImage(image, null, null), param);
            } catch (Exception ex) {
                ex.printStackTrace();
                System.out.println("Exception : " + ex);
                System.exit(0);
            }
            return baos.toByteArray();
        }

        public void setCompressionQuality(float cq) {
            compressionQuality = cq;
            param.setCompressionQuality(compressionQuality);
        }
    }

    //解析RTSP请求，若SETUP：获取到fileName，CSeq，RTP_dest_port
    private int parseRequest() {
        int request_type = -1;
        try {
            //解析请求行，提取 request_type
            String RequestLine = RTSPBufferedReader.readLine();
            System.out.println("RTSP Server - Received from Client:");
            System.out.println(RequestLine);

            StringTokenizer tokens = new StringTokenizer(RequestLine);
            String request_type_string = tokens.nextToken();

            //转换为 request_type
            if ((request_type_string).compareTo("SETUP") == 0)
                request_type = SETUP;
            else if ((request_type_string).compareTo("PLAY") == 0)
                request_type = PLAY;
            else if ((request_type_string).compareTo("PAUSE") == 0)
                request_type = PAUSE;
            else if ((request_type_string).compareTo("TEARDOWN") == 0)
                request_type = TEARDOWN;
            else if ((request_type_string).compareTo("DESCRIBE") == 0)
                request_type = DESCRIBE;

            if (request_type == SETUP) {
                //从RequestLine中提取视频文件名
                VideoFileName = tokens.nextToken();
            }

            //解析SeqNumLine并提取CSeq字段
            String SeqNumLine = RTSPBufferedReader.readLine();
            System.out.println(SeqNumLine);
            tokens = new StringTokenizer(SeqNumLine);
            tokens.nextToken();
            RTSPSeqNb = Integer.parseInt(tokens.nextToken());

            //获取最后一行
            String LastLine = RTSPBufferedReader.readLine();
            System.out.println(LastLine);

            tokens = new StringTokenizer(LastLine);
            if (request_type == SETUP) {
                //找出rtp目的段口
                for (int i = 0; i < 3; i++)
                    tokens.nextToken(); //跳过
                RTP_dest_port = Integer.parseInt(tokens.nextToken());
            } else if (request_type == DESCRIBE) {
                tokens.nextToken();
                String describeDataType = tokens.nextToken();
            } else {
                //其他情况LastLine是RTSPId行
                tokens.nextToken(); //跳过 Session
                RTSPid = tokens.nextToken();
            }
        } catch (Exception ex) {
            System.out.println("Exception : " + ex);
            System.exit(0);
        }

        return (request_type);
    }

    // 为当前媒体创建一个SDP格式的DESCRIBE响应字符串
    private String describe() {
        StringWriter writer1 = new StringWriter();
        StringWriter writer2 = new StringWriter();


        writer2.write("v=0" + CRLF);
        writer2.write("m=video " + RTSP_dest_port + " RTP/AVP " + MJPEG_TYPE + CRLF);
//        writer2.write("m=video " + "1052" + " RTP/AVP " + MJPEG_TYPE + CRLF);
        writer2.write("a=control:streamid=" + RTSPid + CRLF);
        writer2.write("a=mimetype:string;\"video/MJPEG\"" + CRLF);
        String body = writer2.toString();

        writer1.write("Content-Base: " + VideoFileName + CRLF);
        writer1.write("Content-Type: " + "application/sdp" + CRLF);
        writer1.write("Content-Length: " + body.length() + CRLF);
        writer1.write(body);

        return writer1.toString();
    }

    //发送RTSP响应
    private void sendResponse() {
        try {
            RTSPBufferedWriter.write("RTSP/1.0 200 OK" + CRLF);
            RTSPBufferedWriter.write("CSeq: " + RTSPSeqNb + CRLF);
            RTSPBufferedWriter.write("Session: " + RTSPid + CRLF);
            RTSPBufferedWriter.flush();
            System.out.println("RTSP Server - Sent response to Client.");
        } catch (Exception ex) {
            System.out.println("Exception : " + ex);
            System.exit(0);
        }
    }

    private void sendDescribe() {
        String des = describe();
        try {
            RTSPBufferedWriter.write("RTSP/1.0 200 OK" + CRLF);
            RTSPBufferedWriter.write("CSeq: " + RTSPSeqNb + CRLF);
            RTSPBufferedWriter.write(des);
            RTSPBufferedWriter.flush();
            System.out.println("RTSP Server - Sent response to Client.");
        } catch (Exception ex) {
            System.out.println("Exception : " + ex);
            System.exit(0);
        }
    }
}
